package com.beiji.lib.pen.ui.renderer

import android.graphics.*
import android.view.MotionEvent
import com.beiji.lib.pen.PenLog
import com.beiji.lib.pen.cache.PenDotCacheHelper
import com.beiji.lib.pen.model.DotUnit
import com.beiji.lib.pen.model.PenStroke
import com.beiji.lib.pen.utils.DateUtils
import com.tqltech.tqlpencomm.bean.Dot

/**
 * 橡皮擦操作类
 * 为了在不同设备、不同平台之间绘制效果保持一致，橡皮擦的宽度不采用具体数值，而是笔迹宽度的eraserSizeRatio倍
 * Created by X on 2018/6/22.
 */
class EraserRenderer {
    private val path = Path()

    private var preX: Float = 0f
    private var preY: Float = 0f
    private var curX: Float = -1f
    private var curY: Float = -1f
    private var defaultPaint: Paint = Paint()
    private var eraserSize: Float = 40f
    private var eraserSizeRatio: Float = 40f
    var baseStrokeWidth: Float = 1f

    var pageId = 0
    var bookId = 0

    var eraserIndicator: EraserIndicator = EraserIndicator()

    init {
        defaultPaint.apply {
            isAntiAlias = true
            strokeWidth = eraserSize
//            color = Color.BLACK
            style = Paint.Style.STROKE
            strokeJoin = Paint.Join.ROUND
            strokeCap = Paint.Cap.ROUND
            strokeMiter = 0.3f
            xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
        }
    }

    fun setEraserRadius(radiusRatio: Float) {
        val radius = radiusRatio * baseStrokeWidth
        eraserSizeRatio = radiusRatio * 2
        eraserSize = eraserSizeRatio * baseStrokeWidth
        defaultPaint.strokeWidth = eraserSize
        eraserIndicator.radius = radius
    }

    fun draw(canvas: Canvas, stroke: PenStroke, dotMatrix: Matrix, paint: Paint?) {
        val path = Path()
        for (i in stroke.list.indices) {
            val point = stroke.list[i]
            if (i == 0) {
                defaultPaint.strokeWidth = point.f * baseStrokeWidth
                path.moveTo(point.x.toFloat(), point.y.toFloat())
            } else {
                val lastPoint = stroke.list[i - 1]
                path.quadTo(lastPoint.x.toFloat(), lastPoint.y.toFloat(), (lastPoint.x + point.x) / 2f, (lastPoint.y + point.y) / 2f)
            }
        }
        path.transform(dotMatrix)
        canvas.drawPath(path, defaultPaint)
    }

    fun draw(canvas: Canvas, dot: DotUnit, paint: Paint?) {
        if (dot.actionType != PenStroke.ACTION_ERASER) {
            return
        }
//        PenLog.i("Eraser:" + dot.toString())
        curX = dot.x.toFloat()
        curY = dot.y.toFloat()

        if (dot.type == Dot.DotType.PEN_DOWN || preX == 0f) {
            path.reset()
            path.moveTo(curX, curY)
        } else {
            path.quadTo(preX, preY, (preX + curX) / 2, (preY + curY) / 2)
        }
        preX = curX
        preY = curY
        if (paint != null) {
            paint.strokeWidth = dot.force * baseStrokeWidth
            canvas.drawPath(path, paint)
        } else {
            defaultPaint.strokeWidth = dot.force * baseStrokeWidth
            canvas.drawPath(path, defaultPaint)
        }
    }

    fun onTouchEvent(canvas: Canvas, bitmapMatrix: Matrix, dotMatrix: Matrix, event: MotionEvent): Boolean {
        val scaledEraserSizeRatio = doTransformEraserSize(eraserSizeRatio, bitmapMatrix)
        PenLog.i("EraserWidth:${defaultPaint.strokeWidth} -> ${defaultPaint.strokeWidth.toInt()}")
        val dot = DotUnit().apply {
            timestamp = DateUtils.getCurrentTimeMillis()
            x = event.x
            y = event.y
            pageId = this@EraserRenderer.pageId
            bookId = this@EraserRenderer.bookId
            actionType = PenStroke.ACTION_ERASER
            force = scaledEraserSizeRatio
        }
        eraserIndicator.apply {
            centerX = event.x
            centerY = event.y
        }
        when (event.action and MotionEvent.ACTION_MASK) {
            MotionEvent.ACTION_DOWN -> {
                dot.type = Dot.DotType.PEN_DOWN
                PenDotCacheHelper.cacheDot(mapScreenToPaper(dot, dotMatrix, bitmapMatrix))
                draw(canvas, doTransformDot(dot, bitmapMatrix), null)
                eraserIndicator.isVisible = true
            }
            MotionEvent.ACTION_POINTER_DOWN -> {
                //第二个手指落下时即隐藏橡皮擦
                eraserIndicator.isVisible = false
            }
            MotionEvent.ACTION_MOVE -> {
                dot.type = Dot.DotType.PEN_MOVE
                PenDotCacheHelper.cacheDot(mapScreenToPaper(dot, dotMatrix, bitmapMatrix))
                draw(canvas, doTransformDot(dot, bitmapMatrix), null)
                eraserIndicator.isVisible = true
            }
            MotionEvent.ACTION_UP -> {
                dot.type = Dot.DotType.PEN_UP
                PenDotCacheHelper.cacheDot(mapScreenToPaper(dot, dotMatrix, bitmapMatrix))
                draw(canvas, doTransformDot(dot, bitmapMatrix), null)
                eraserIndicator.isVisible = false
            }
        }
        return true
    }


    /**
     * 由于画布会被缩放，那么橡皮擦擦除区域的大小也会跟着缩放，产品要求不跟着缩放，那Paint的宽度就得按照画布的缩放进行逆向变换，以保证在缩放后的画布上，橡皮擦的大小始终不变
     */
    private fun doTransformEraserSize(strokeWidth: Float, bitmapMatrix: Matrix): Float {
        val matrixArray = FloatArray(9)
        bitmapMatrix.getValues(matrixArray)
        return strokeWidth / matrixArray[0]
    }

    /**
     * 根据画布的缩放比例，调整橡皮擦指示器的半径
     */
    private fun doTransformIndicatorRadius(radius: Float, bitmapMatrix: Matrix): Float {
        val matrixArray = FloatArray(9)
        bitmapMatrix.getValues(matrixArray)
        return matrixArray[0] * radius
    }

    /**
     * 将屏幕上的橡皮擦坐标通过矩阵变换，映射为本子上的坐标，以便存储
     * @param dot 橡皮擦的坐标
     * @param dotMatrix 本子实际坐标和画板坐标的变换矩阵
     */
    private fun mapScreenToPaper(dot: DotUnit, dotMatrix: Matrix, bitmapMatrix: Matrix): DotUnit {
        val dotInOriginCanvas = StrokeTransformHelper.invertDot(dot, bitmapMatrix)
        return StrokeTransformHelper.invertDot(dotInOriginCanvas, dotMatrix)
    }

    /**
     * 将屏幕上橡皮擦的坐标通过矩阵变换，得到在原始画布（未缩放）上的坐标
     * @param dot 橡皮擦的坐标
     * @param bitmapMatrix 画布当前矩阵
     */
    private fun doTransformDot(dot: DotUnit, bitmapMatrix: Matrix): DotUnit {
        return StrokeTransformHelper.invertDot(dot, bitmapMatrix)
    }

    /**
     * 橡皮擦指示器参数
     */
    class EraserIndicator {
        var centerX: Float = -1f
        var centerY: Float = -1f
        var radius: Float = 20f
        var eraserIndicatorPaint: Paint = Paint()
        var isVisible: Boolean = false

        init {
            eraserIndicatorPaint.apply {
                isAntiAlias = true
                strokeWidth = 1f
                color = Color.LTGRAY
                style = Paint.Style.FILL_AND_STROKE
                strokeJoin = Paint.Join.ROUND
                strokeCap = Paint.Cap.ROUND
                strokeMiter = 0.3f
            }
        }
    }
}